"""Test Zeroconf multiple instance protection."""

from __future__ import annotations

from typing import Self
from unittest.mock import Mock, patch

import pytest

from homeassistant.components.zeroconf import async_get_instance
from homeassistant.components.zeroconf.usage import install_multiple_zeroconf_catcher
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component

from tests.common import extract_stack_to_frame

DOMAIN = "zeroconf"


class MockZeroconf:
    """Mock Zeroconf class."""

    def __init__(self, *args, **kwargs) -> None:
        """Initialize the mock."""

    def __new__(cls, *args, **kwargs) -> Self:
        """Return the shared instance."""


@pytest.mark.usefixtures("mock_async_zeroconf", "mock_zeroconf")
async def test_multiple_zeroconf_instances(
    hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
    """Test creating multiple zeroconf throws without an integration."""
    assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})

    zeroconf_instance = await async_get_instance(hass)

    with patch("zeroconf.Zeroconf", MockZeroconf):
        install_multiple_zeroconf_catcher(zeroconf_instance)

        new_zeroconf_instance = MockZeroconf()
        assert new_zeroconf_instance == zeroconf_instance

        assert "Zeroconf" in caplog.text


@pytest.mark.usefixtures("mock_async_zeroconf", "mock_zeroconf")
async def test_multiple_zeroconf_instances_gives_shared(
    hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
    """Test creating multiple zeroconf gives the shared instance to an integration."""
    assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})

    zeroconf_instance = await async_get_instance(hass)

    with patch("zeroconf.Zeroconf", MockZeroconf):
        install_multiple_zeroconf_catcher(zeroconf_instance)

        correct_frame = Mock(
            filename="/config/custom_components/burncpu/light.py",
            lineno="23",
            line="self.light.is_on",
        )
        with (
            patch(
                "homeassistant.helpers.frame.linecache.getline",
                return_value=correct_frame.line,
            ),
            patch(
                "homeassistant.helpers.frame.get_current_frame",
                return_value=extract_stack_to_frame(
                    [
                        Mock(
                            filename="/home/dev/homeassistant/core.py",
                            lineno="23",
                            line="do_something()",
                        ),
                        correct_frame,
                        Mock(
                            filename="/home/dev/homeassistant/components/zeroconf/usage.py",
                            lineno="23",
                            line="self.light.is_on",
                        ),
                        Mock(
                            filename="/home/dev/mdns/lights.py",
                            lineno="2",
                            line="something()",
                        ),
                    ]
                ),
            ),
        ):
            assert MockZeroconf() == zeroconf_instance

        assert "custom_components/burncpu/light.py" in caplog.text
        assert "23" in caplog.text
        assert "self.light.is_on" in caplog.text
